package pl.asoft.nbp.service.impl;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pl.asoft.nbp.dao.CurrencyDAO;
import pl.asoft.nbp.dao.ExchangeRateDAO;
import pl.asoft.nbp.model.Currency;
import pl.asoft.nbp.model.ExchangeRate;
import pl.asoft.nbp.service.ExchangeRateService;
import pl.asoft.nbp.service.NbpService;

import java.util.Date;
import java.util.List;

import static pl.asoft.nbp.validator.ExchangeRateValidator.validateDates;
import static pl.asoft.nbp.validator.ExchangeRateValidator.validateShortName;

/**
 * Created by rbiedrawa on 2014-12-02.
 */
@Service("exchangeRateService")
@Transactional(readOnly = true)
public class ExchangeRateServiceImpl implements ExchangeRateService {
    private ExchangeRateDAO exchangeRateDAO;
    private NbpService nbpService;
    private CurrencyDAO currencyDAO;

    @Autowired
    public ExchangeRateServiceImpl(ExchangeRateDAO exchangeRateDAO, NbpService nbpService, CurrencyDAO currencyDAO) {
        this.exchangeRateDAO = exchangeRateDAO;
        this.nbpService = nbpService;
        this.currencyDAO = currencyDAO;
    }

    @Override
    public List<ExchangeRate> findAllExistingExchangeRates() {
        return exchangeRateDAO.findAllExistingEagerLoad();
    }

    @Override
    public List<ExchangeRate> findActualExchangeRates() {
        List<ExchangeRate> exchangeRates = exchangeRateDAO.findByDate(new Date());
        if (exchangeRates.isEmpty()) {
            exchangeRates = (List<ExchangeRate>) nbpService.getExchangeRate("ALL",null,null);
            //Add to database, actual records (better option Quartz job, running after NBP update site)
            //exchangeRateDAO.saveOrUpdate(exchangeRates);
        }
        return exchangeRates;
    }

    @Override
    public ExchangeRate findActualExchangeRate(String currencyCode) {
        validateShortName(currencyCode);
        ExchangeRate exchangeRate = exchangeRateDAO.findByShortNameAndDate(currencyCode.toUpperCase(), new Date());

        if (exchangeRate == null) {
            exchangeRate = nbpService.getExchangeRate(currencyCode.toUpperCase());
        }
        return exchangeRate;

    }

    @Override
    public List<ExchangeRate> findAllExistingExchangeRates(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findByShortName(currencyCode.toUpperCase());
    }

    @Override
    public ExchangeRate findByCurrencyCodeAndDate(String currencyCode, Date date) {
        validateShortName(currencyCode);
        if(date == null){
            throw new IllegalArgumentException("Date cannot be null");
        }

        ExchangeRate exchangeRate = exchangeRateDAO.findByShortNameAndDate(currencyCode.toUpperCase(), date);

        DateTime dateTime = new DateTime(date);
        //if not found and date is actual, get Exchange rate from site
        if (exchangeRate == null && dateTime.isEqualNow()) {
            exchangeRate = nbpService.getExchangeRate(currencyCode.toUpperCase());
        }

        return exchangeRate;
    }

    @Override
    public List<ExchangeRate> findAllByDateBetween(Date startDate, Date endDate) {
        validateDates(startDate,endDate);
        return exchangeRateDAO.findAllByDateBetween("ALL", startDate, endDate);
    }

    @Override
    public List<ExchangeRate> findAllByDateBetween(String currencyCode, Date startDate, Date endDate) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findAllByDateBetween(currencyCode.toUpperCase(), startDate, endDate);
    }

    @Override
    public Double getAverageForCurrencyCode(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findExchangeRateAverage(currencyCode.toUpperCase());
    }

    @Override
    public Double getAverageForCurrencyCode(String currencyCode, Date startDate, Date endDate) {
        validateShortName(currencyCode);
        validateDates(startDate,endDate);
        return exchangeRateDAO.findExchangeRateAverage(currencyCode.toUpperCase(),startDate,endDate);
    }

    @Override
    public List<ExchangeRate> withMaxPriceForCurrencyCode(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findExchangeRateWithMaxPrice(currencyCode.toUpperCase());

    }

    @Override
    public List<ExchangeRate> withMaxPriceForCurrencyCode(String currencyCode, Date startDate, Date endDate) {
        validateShortName(currencyCode);
        validateDates(startDate,endDate);
        return exchangeRateDAO.findExchangeRateWithMaxPrice(currencyCode.toUpperCase(), startDate, endDate);
    }

    @Override
    public List<ExchangeRate> withMinPriceForCurrencyCode(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findExchangeRateWithMinPrice(currencyCode.toUpperCase());

    }

    @Override
    public List<ExchangeRate> withMinPriceForCurrencyCode(String currencyCode, Date startDate, Date endDate) {
        validateShortName(currencyCode);
        validateDates(startDate,endDate);

        return exchangeRateDAO.findExchangeRateWithMinPrice(currencyCode.toUpperCase(), startDate, endDate);
    }

    @Override
    public List<ExchangeRate> getAllAboveAverage(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findAllAboveAverage(currencyCode.toUpperCase());
    }

    @Override
    public List<ExchangeRate> getAllBelowAverage(String currencyCode) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findAllBelowAverage(currencyCode.toUpperCase());
    }

    @Override
    public List<ExchangeRate> getBestExchangeRates(String currencyCode, int resultLimit) {
        validateShortName(currencyCode);
        return exchangeRateDAO.findAllWithMaxPriceLimit(currencyCode.toUpperCase(),resultLimit);
    }

    @Override
    public List<ExchangeRate> getWorstExchangeRates(String currencyCode, int resultLimit) {
        validateShortName(currencyCode);

        return exchangeRateDAO.findAllWithMinPriceLimit(currencyCode.toUpperCase(), resultLimit);
    }

    @Override
    public List<ExchangeRate> getExchangeRateForDate(Date date) {
        if(date == null){
            throw new IllegalArgumentException("Date cannot be null");
        }
        return exchangeRateDAO.findByDate(date);
    }

    @Override
    @Transactional(readOnly = false)
    public ExchangeRate addExchangeRate(ExchangeRate exchangeRate) {

        validateShortName(exchangeRate.getCurrency().getShortName());
       
        currencyDAO.saveOrUpdate(exchangeRate.getCurrency());

        return exchangeRateDAO.save(exchangeRate);
    }

    @Override
    @Transactional(readOnly = false)
    public void addExchangeRate(List<ExchangeRate> exchangeRates) {
        for(ExchangeRate exchangeRate : exchangeRates){
            addExchangeRate(exchangeRate);
        }
    }

    @Override
    @Transactional(readOnly = false)
    public ExchangeRate updateExchangeRate(ExchangeRate exchangeRate) {
        if(exchangeRate.getId() == null){
            throw new IllegalArgumentException("ExchangeRate doesn't exist in database. Please saveOrUpdate entity before update.");
        }

        if(exchangeRate.getCurrency() == null){
            throw new IllegalArgumentException("Currency cannot be null");
        }

        validateShortName(exchangeRate.getCurrency().getShortName());

       currencyDAO.saveOrUpdate(exchangeRate.getCurrency());


        return exchangeRateDAO.update(exchangeRate);
    }

    @Override
    @Transactional(readOnly = false)
    public void removeExchangeRate(ExchangeRate exchangeRate) {
        Long id = exchangeRate.getId();
        if (id != null) {
            exchangeRateDAO.delete(id);
        }
    }
}
